/*
 *	OpenSource code and fragments by avocado systems, Marcus Fuhrmeister
 *
 *	Redistribution and use in source and binary forms, with or without
 *	modification, are permitted provided that the following conditions
 *	are met:
 *  
 *		* Redistribution of source code must retain the above OpenSource comment,
 *		this list of conditions and the following disclaimer.
 * 
 *		* Redistribution in binary form must reproduce the above OpenSource comment,
 *		this list of conditions and the following disclaimer in the
 *		documentation and/or other materials provided with the distribution.
 * 
 *	Neither the name of 'avocaod systems, Marcus Fuhrmeister' or the
 *	names of contributors may be used to endorse or promote products derived
 *	from this software without specific prior written permission.
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 *	@project	BrowserChooser 0.2
 *	@package	de.fuhrmeister.browserchooser.listener
 *	@file		DockletController.java
 *
 */
package de.fuhrmeister.browserchooser.controller;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.net.URL;
import java.util.Iterator;
import java.util.TooManyListenersException;

import javax.swing.JFrame;

//import de.fuhrmeister.browserchooser.animation.Animator;
import de.fuhrmeister.browserchooser.model.BrowserModel;
import de.fuhrmeister.browserchooser.model.ModelFactory;
import de.fuhrmeister.browserchooser.view.Docklet;
import de.fuhrmeister.util.ConfigManager;
import de.fuhrmeister.util.ImageManager;
import de.fuhrmeister.util.Tools;

/**
 * Controller class handling the main view.
 * 
 * @date 20.11.2008
 * @author Marcus Fuhrmeister
 */
public class DockletController implements MouseListener, DropTargetListener, WindowFocusListener {
	private static DockletController self;
	private Docklet docklet;
//	private boolean isDragged;
	private boolean dragHasAlreadyEntered = false;

	/**
	 * Singleton pattern.
	 * 
	 * @return this
	 */
	public static synchronized DockletController getInstance() {
		if (self == null) {
			DockletController.self = new DockletController();
			return DockletController.self;
		}
		return DockletController.self;
	}

	/**
	 * Inits the main view.
	 * Sets <code>IconImage</code> and title, size and location as well as
	 * extra preferences.
	 * Calls to set the view.
	 */
	private void init() {
		docklet = new Docklet();
		// for launch4j executable wrapper
		docklet.setIconImage(ImageManager.getInstance().getImage(ImageManager.APPLICATION));
		// for launch4j executable wrapper
		docklet.setTitle("BrowserChooser");
		final int docklet_width;
		final int docklet_height = Integer.parseInt(Tools.configMng.getPreference(ConfigManager.DOCKLET_ICON_SIZE)) + Integer.parseInt(Tools.configMng.getConfiguration(ConfigManager.DOCKLET_ICON_SPACE));
		if (ModelFactory.modelExists()) {
			docklet_width = ModelFactory.getBrowserModelCount() * docklet_height + (Integer.parseInt(Tools.configMng.getConfiguration(ConfigManager.DOCKLET_ICON_SPACE)) * 2 );
		} else {
			docklet_width = docklet_height + Integer.parseInt(Tools.configMng.getConfiguration(ConfigManager.DOCKLET_ICON_SPACE));
		}
		docklet.setSize(this.getDockletSize(docklet_width, docklet_height, Tools.configMng.getPreference(ConfigManager.DOCKLET_LOCATION)));
		docklet.setLocation(this.getDockletLocation(Tools.configMng.getPreference(ConfigManager.DOCKLET_LOCATION)));
		docklet.setAlwaysOnTop(Boolean.parseBoolean(Tools.configMng.getPreference(ConfigManager.DOCKLET_ALWAYSONTOP)));
		
		docklet.setFlowLayout();
		
		setListeners();
		setView();
	}

	/**
	 * Register the listeners
	 */
	private void setListeners() {
		docklet.registerListener(this);
		final DropTarget dt = new DropTarget();
		try {
			dt.addDropTargetListener(this);
		} catch (final TooManyListenersException e) {
			e.printStackTrace();
		}
		docklet.setDropTarget(dt);
		docklet.addWindowFocusListener(this);
	}

	/**
	 * Calls to add the browser views to the main view if the model exists.
	 */
	private synchronized void setView() {
		if (docklet.getBrowserShortcutCount() >= 1) {
			docklet.removeBrowserShortcuts();
		}
		if (ModelFactory.modelExists()) {
			final Iterator<BrowserModel> i = ModelFactory.getBrowserModels();
			while (i.hasNext()) {
				final BrowserModel bm = i.next();
				final int iconsize = Integer.parseInt(Tools.configMng.getPreference(ConfigManager.DOCKLET_ICON_SIZE));
				docklet.addBrowserShortcut(BrowserShortcutViewController.getInstance().getView(Tools.imageMng.createIcon((bm.getIcon() != null && bm.getIcon() != "") ? bm.getIcon() : null, iconsize, (bm.get(0).getExec() != null && bm.get(0).getExec() != "") ? bm.get(0).getExec() : null), bm.getID(), iconsize));
			}
		}
	}

	/**
	 * Gets the size of the view under regard of the location.
	 * 
	 * @param width the current width of the view
	 * @param height the current height of the view
	 * @param loc the location of the view on the screen
	 * @return the new dimension of the view
	 */
	private Dimension getDockletSize(final int width, final int height, final String loc) {
		// TOP, BOTTOM Location
		if (loc.equalsIgnoreCase(ConfigManager.DOCKLET_LOCATION_TOP) || loc.equalsIgnoreCase(ConfigManager.DOCKLET_LOCATION_BOTTOM)) {
			if (width >= height) {
				return new Dimension(width, height);
			} else {
				return new Dimension(height, width);
			}
		}
		// RIGHT, LEFT Location
		else if (loc.equalsIgnoreCase(ConfigManager.DOCKLET_LOCATION_RIGHT) || loc.equalsIgnoreCase(ConfigManager.DOCKLET_LOCATION_LEFT)) {
			if (width >= height) {
				return new Dimension(height, width);
			} else {
				return new Dimension(width, height);
			}
		} else {
			return new Dimension(width, height);
		}
	}

	/**
	 * Returns the location point of the view under regard of the location.
	 * 
	 * @param loc
	 * @return
	 */
	private Point getDockletLocation(final String loc) {
		// TOP Location
		if (loc.equalsIgnoreCase(ConfigManager.DOCKLET_LOCATION_LEFT)) {
			return new Point(0, Controller.SYSTEM_SCREEN_HEIGHT / 2 - docklet.getHeight() / 2);
		}
		// RIGHT Location
		else if (loc.equalsIgnoreCase(ConfigManager.DOCKLET_LOCATION_RIGHT)) {
			return new Point((Controller.SYSTEM_SCREEN_WIDTH - docklet.getWidth()), Controller.SYSTEM_SCREEN_HEIGHT / 2 - docklet.getHeight() / 2);
		}
		// BOTTOM Location
		else if (loc.equalsIgnoreCase(ConfigManager.DOCKLET_LOCATION_BOTTOM)) {
			return new Point(Controller.SYSTEM_SCREEN_WIDTH / 2 - docklet.getWidth() / 2, (Controller.SYSTEM_SCREEN_HEIGHT - docklet.getHeight()));
		}
		// LEFT Location
		else {
			return new Point(Controller.SYSTEM_SCREEN_WIDTH / 2 - docklet.getWidth() / 2, 0);
		}
	}


	/**
	 * This method implements the cross-platform mouse event for the popup trigger.
	 * Unfortunately Windos and Unix systems handle the mouse event for the popup trigger
	 * in different ways. On Windows the trigger appears on <code>mouseReleased()</code>,
	 * on Unix it is  <code>mousePressed()</code>.
	 * On Ubuntu linux 7.10 OS there is a bug (or a feature) so that neither <code>mouseReleased()</code>,
	 * nor <code>mousePressed()</code> trigger the popup. Therefore this method uses the MouseEvent masks
	 * to work around it.
	 * 
	 * @see URL http://forums.java.net/jive/thread.jspa?threadID=35178
	 * @param me the <code>MouseEvent</code> that maybe the popup trigger
	 */
	private final void evaluatePopupTrigger(MouseEvent me) {
		 if (me.isPopupTrigger()) { 
			 DockletPopupMenuController.getInstance().show(docklet, me.getPoint().x, me.getPoint().y);
		 } else if ((me.getButton() == MouseEvent.BUTTON3) && (me.getID() == MouseEvent.MOUSE_RELEASED) ) {
			 //TODO: implementation
		 }
	}

	/**
	 * Sets the view visible.
	 */
	public void show() {
		if (docklet == null) {
			this.init();
		}
		docklet.setVisible(true);
		docklet.setBackground(this.getDockletBackgroundColor());
	}

	public Color getDockletBackgroundColor() {
		try {
				return Color.decode(this.getDockletBackgroundColorAsHexString());
		} catch (NumberFormatException nfe){
			Tools.logManager.finest(Tools.configMng.getStringFor(ConfigManager.MESSAGE_VALUE_ERROR_SETTINGCOLOR)+ ": "+nfe.getLocalizedMessage());
			return Color.LIGHT_GRAY;
		}
	}
	
	public String getDockletBackgroundColorAsHexString() {
		return "0x"+Tools.configMng.getPreference(ConfigManager.DOCKLET_BACKGROUND);
	}

	/**
	 * Calls to init the view und update the GUI.
	 */
	public void updateGUI() {
		docklet.setVisible(false);
		this.init();
		DockletPopupMenuController.getInstance().updateGUI();
		docklet.setVisible(true);
		docklet.setBackground(this.getDockletBackgroundColor());
	}

	/**
	 * Requests the focus for this view.
	 */
	public void focus() {
		docklet.requestFocus();
		docklet.toFront();
	}

	/**
	 * Returns the view.
	 * 
	 * @return the view or null.
	 */
	public JFrame getWindow() {
		if (docklet != null) {
			return docklet;
		} else {
			return null;
		}
	}

	/**
	 * Disposes the view.
	 */
	public void exit() {
		docklet.setVisible(false);
		docklet.dispose();
	}
	

//	/**
//	 * TODO: preliminary design for next major version
//	 */
//	@SuppressWarnings("unused")
//	private void indicateDragAndDrop(final boolean dragOver, final Point location) {
//		final boolean animate_width = !(Tools.configMng.getPreference(ConfigManager.DOCKLET_LOCATION).equalsIgnoreCase(ConfigManager.DOCKLET_LOCATION_LEFT) || Tools.configMng.getPreference(ConfigManager.DOCKLET_LOCATION).equalsIgnoreCase(ConfigManager.DOCKLET_LOCATION_RIGHT));
//		final int value = Integer.parseInt(Tools.configMng.getPreference(ConfigManager.DOCKLET_ICON_SIZE)) + Integer.parseInt(Tools.configMng.getConfiguration(ConfigManager.DOCKLET_ICON_SPACE));
//		final int speed = Integer.parseInt(Tools.configMng.getPreference(ConfigManager.ANIMATION_SPEED));
//		final int delay = Integer.parseInt(Tools.configMng.getPreference(ConfigManager.ANIMATION_DELAY));
//		if (dragOver) {
//			if (!this.isDragged) {
//				this.isDragged = true;
//				if (animate_width) {
//					if ( (docklet.getWidth() + value) >= Controller.SYSTEM_SCREEN_WIDTH ) {
//						Animator.setAnimation(Animator.ANIMATE_COMPONENTS_SHRINK, docklet, value);
//						Animator.start(speed, delay);
//
//					} else {
//						Animator.setAnimation(Animator.ANIMATE_WIDTH_ENLARGE, docklet, value);
//						Animator.start(speed, delay);
//					}
//				} else {
//					if ((docklet.getHeight() + value) >= Controller.SYSTEM_SCREEN_HEIGHT) {
//						Animator.setAnimation(Animator.ANIMATE_COMPONENTS_SHRINK, docklet, value);
//						Animator.start(speed, delay);
//
//					} else {
//						Animator.setAnimation(Animator.ANIMATE_HEIGHT_ENLARGE, docklet, value);
//						Animator.start(speed, delay);
//					}
//				}
//			}
//		} else {
//			Animator.setAnimation(Animator.ANIMATE_WIDTH_SHRINK, docklet, value);
//			Animator.start(speed, delay);
//			this.isDragged = false;
//		}
//	}

	/**
	 * Calls to evaluate a popup trigger.
	 */
	public void mouseReleased(final MouseEvent me) {
		this.evaluatePopupTrigger(me);
	}

	public void mouseClicked(final MouseEvent me) {
	}

	public void mouseEntered(final MouseEvent me) {
	}

	public void mouseExited(final MouseEvent me) {
	}

	public void mousePressed(final MouseEvent me) {
		this.evaluatePopupTrigger(me);
	}

	/**
	 * TODO: preliminary design for next major version
	 */
	public final void dragEnter(final DropTargetDragEvent dtde) {
		if (Boolean.parseBoolean(Tools.configMng.getPreference(ConfigManager.ANIMATION_ENABLED))) {
		if (!this.dragHasAlreadyEntered ) {
			this.dragHasAlreadyEntered = true;
//			this.indicateDragAndDrop(true, dtde.getLocation());
		}
		}
		dtde.acceptDrag(DnDConstants.ACTION_COPY);
	}
	/**
	 * TODO: preliminary design for next major version
	 */
	public final void dragOver(final DropTargetDragEvent dtde) {
		dtde.acceptDrag(DnDConstants.ACTION_COPY);
	}

	/**
	 * TODO: preliminary design for next major version
	 */
	public final void dropActionChanged(final DropTargetDragEvent dtde) {
	}

	/**
	 * TODO: preliminary design for next major version
	 */
	public final void dragExit(final DropTargetEvent dte) {
		if (Boolean.parseBoolean(Tools.configMng.getPreference(ConfigManager.ANIMATION_ENABLED))) {
		Point mousePointer = MouseInfo.getPointerInfo().getLocation();
		mousePointer.x -= docklet.getX();
		mousePointer.y -= docklet.getY();
		if ( !docklet.contains(mousePointer) ) {
			this.dragHasAlreadyEntered = false;
//			this.indicateDragAndDrop(false, null);
		}
		else {
			// bewegung innerhalb des Docklets -> hier die Icon bewegung animieren
		}
		}
	}

	/**
	 * Calls to drop the browser executable.
	 */
	public final void drop(final DropTargetDropEvent dtde) {
//		this.indicateDragAndDrop(false, dtde.getLocation());
		Controller.getInstance().dropBrowser(dtde);
	}

	public void windowGainedFocus(WindowEvent e) {
	}

	/**
	 * Calls to close the popups when the view loses its focus.
	 */
	public void windowLostFocus(WindowEvent e) {
		DockletPopupMenuController.getInstance().close();
		BrowserShortcutViewPopupMenuController.getInstance().close();
	}
}